﻿//-------------------------------------------------------------------
//	Copyright (c) 2012-2014 Zhirnov Andrey
//	This file is part of the "UXGEN-ES" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#include "opengles.h"
#include "Base/Engine.Base.h"

namespace gles
{
	using namespace Engine;
	using namespace Engine::Base;


#ifdef GLES_EMULATE
#	define GLES_SWITCH( _gles, _gl )	_gl
#else
#	define GLES_SWITCH( _gles, _gl )	_gles
#endif

	
// get function address from driver
#define OPENGLES_GET_PROC( p, n, d ) \
	++counter; \
	if ( (n = (p)glesGetProcAddress( #n ))		== null ) /*\
	if ( (n = (p)glesGetProcAddress( #n"EXT" )) == null ) \
	if ( (n = (p)glesGetProcAddress( #n"OES" )) == null )*/ \
	{ \
		n = d; \
		missing++; \
	}

	
// Default Functions //
#undef  GLES_BUILD_DEFFUNC
#define GLES_BUILD_DEFFUNC( _retType, _funcName, _funcParams, _retValue ) \
	_retType GLES_APIENTRY glDefault##_funcName _funcParams \
	{ \
		LOG( "Used dummy function "#_funcName, ELog::DEBUG ); \
		return _retValue; \
	}
	
// pointers to functions //
#undef  GLES_BUILDFUNC
#define GLES_BUILDFUNC( _retType, _funcName, _funcParams, _retValue ) \
	PFNGL##_funcName##PROC	gl##_funcName = null;
	
// constants //
#undef  GLES_BUILDCONST
#define GLES_BUILDCONST( _type, _name ) \
	_type	C_GL_##_name = _type();



	GLES_CORE_FUNCTIONS( GLES_BUILD_DEFFUNC )
	GLES_CORE_FUNCTIONS( GLES_BUILDFUNC )
	GLES_CONSTANTS_I( GLES_BUILDCONST )
			
	namespace OES_get_program_binary {
		GLES_GET_PROGRAM_BINARY( GLES_BUILD_DEFFUNC )
		GLES_GET_PROGRAM_BINARY( GLES_BUILDFUNC )
	}
	namespace OES_texture_3D {
		GLES_TEXTURE_3D( GLES_BUILD_DEFFUNC )
		GLES_TEXTURE_3D( GLES_BUILDFUNC )
		GLES_BUILDCONST( GLint, MAX_3D_TEXTURE_SIZE )
	}
	namespace OES_vertex_array_object {
		GLES_VERTEX_ARRAY_OBJECT( GLES_BUILD_DEFFUNC )
		GLES_VERTEX_ARRAY_OBJECT( GLES_BUILDFUNC )
	}
	namespace EXT_debug_label {
		GLES_DEBUG_LABEL( GLES_BUILD_DEFFUNC )
		GLES_DEBUG_LABEL( GLES_BUILDFUNC )
	}
	namespace EXT_debug_marker {
		GLES_DEBUG_MARKER( GLES_BUILD_DEFFUNC )
		GLES_DEBUG_MARKER( GLES_BUILDFUNC )
	}
	namespace EXT_discard_framebuffer {
		GLES_DISCARD_FRAMEBUFFER( GLES_BUILD_DEFFUNC )
		GLES_DISCARD_FRAMEBUFFER( GLES_BUILDFUNC )
	}
	namespace EXT_multisampled_render_to_texture {
		GLES_MULTISAMPLED_RENDER_TO_TEXTURE( GLES_BUILD_DEFFUNC )
		GLES_MULTISAMPLED_RENDER_TO_TEXTURE( GLES_BUILDFUNC )
		GLES_BUILDCONST( GLint, MAX_SAMPLES )
	}
	namespace EXT_multi_draw_arrays {
		GLES_MULTI_DRAW_ARRAYS( GLES_BUILD_DEFFUNC )
		GLES_MULTI_DRAW_ARRAYS( GLES_BUILDFUNC )
	}
	namespace EXT_occlusion_query_boolean {
		GLES_OCCLUSION_QUERY_BOOLEAN( GLES_BUILD_DEFFUNC )
		GLES_OCCLUSION_QUERY_BOOLEAN( GLES_BUILDFUNC )
	}
	namespace EXT_robustness {
		GLES_ROBUSTNESS( GLES_BUILD_DEFFUNC )
		GLES_ROBUSTNESS( GLES_BUILDFUNC )
	}
	namespace EXT_separate_shader_objects {
		GLES_SEPARATE_SHADER_OBJECTS( GLES_BUILD_DEFFUNC )
		GLES_SEPARATE_SHADER_OBJECTS( GLES_BUILDFUNC )
	}
	namespace EXT_texture_storage {
		GLES_TEXTURE_STORAGE( GLES_BUILD_DEFFUNC )
		GLES_TEXTURE_STORAGE( GLES_BUILDFUNC )
	}
	namespace EXT_texture_filter_anisotropic {
		GLES_BUILDCONST( GLfloat, MAX_TEXTURE_MAX_ANISOTROPY )
	}



	static LibOpenGL	__libOpenGL;

	void _ClearExtensions();
	
/*
=================================================
	glesGetProcAddress
=================================================
*/
	static void * glesGetProcAddress (const char *address)
	{
		return __libOpenGL.GetProc( address );
	}

/*
=================================================
	GLES_ClearErrors
----
	clear errors stack
=================================================
*/
	GLES_API void GLES_ClearErrors (int maxErros)
	{
		for (int i = 0; i < maxErros && glGetError() != GL_NO_ERROR; ++i) {}
	}
	
/*
=================================================
	GLES_CheckErrors
----
	check error and write to log
=================================================
*/
	GLES_API bool GLES_CheckErrors (const UX_STL::UXTypes::uni_c_string &msg, int maxErrors)
	{
		GLenum	err;
		bool	ret = true;
		string	str("OpenGLES error: ");
		usize	len = str.Length();

		for (int i = 0; i < maxErrors && (err = glGetError()) != GL_NO_ERROR; ++i)
		{
			str.SetLength( len );

			switch ( err )
			{
				case GL_INVALID_ENUM:
					str << "GL_INVALID_ENUM";
					break;

				case GL_INVALID_VALUE:
					str << "GL_INVALID_VALUE";
					break;

				case GL_INVALID_OPERATION:
					str << "GL_INVALID_OPERATION";
					break;

				case GL_INVALID_FRAMEBUFFER_OPERATION:
					str << "GL_INVALID_FRAMEBUFFER_OPERATION";
					break;

				case GL_OUT_OF_MEMORY:
					str << "GL_OUT_OF_MEMORY";
					break;

				default:
					str << "code 0x" << string().FormatI( err, 16 );
					break;
			}

			str << msg;

			LOG( str.cstr(), ELog::ERROR );

			ret = false;
		}

		return ret;
	}
	
/*
=================================================
	GLES_CheckErrors2
----
	check error and write to log
=================================================
*/
	GLES_API bool GLES_CheckErrors2 (const char *glcall, const char *func, const char *file, int line)
	{
		string	msg(", in ");

		msg << glcall << ", function " << func << ", file: \"" << file
			<< "\", line: " << string().FormatI(line, 10);

		return GLES_CheckErrors( msg, 1 );
	}

/*
=================================================
	_IsExtSupported
=================================================
*/
	static UXTypes::uni_c_string	s_sExtensions;

	bool _IsExtSupported (const char *pName)
	{
		if ( s_sExtensions.Empty() ) {
			GL_CALL( s_sExtensions = UXTypes::uni_c_string( (const char *) glGetString( GL_EXTENSIONS ) ) );
		}

		UXTypes::usize	pos = 0;
		return s_sExtensions.Find( pName, pos, 0 );
	}
	
/*
=================================================
	GLES_GetVersion
=================================================
*/
	static int	_glesVersion = 0;

	int _ParseGLESVersion ()
	{
		const char *	ver = null;
		int				res = 0;
		GL_CALL( ver = (const char *) glGetString( GL_VERSION ) );

		if ( ver == null )
			return 0;

		// format: OpenGL<space>ES<space><version number><space><vendor-specific information>

		while ( *ver != 0 and not ( *ver >= '0' and *ver <= '9' ) )		{ ++ver; }

		if ( *ver == 0 )
			return 0;

		res = ( *(ver++) - '0' ) * 100;		// major
		
		while ( *ver != 0 and not ( *ver >= '0' and *ver <= '9' ) )		{ ++ver; }

		if ( *ver == 0 )
			return res;

		res += (*ver - '0') * 10;	// min

		return res;
	}

	GLES_API int  GLES_GetVersion ()
	{
		return _glesVersion;
	}

/*
=================================================
	GLES_Init
=================================================
*/
	GLES_API bool GLES_Init ()
	{
		int	missing = 0;	// number of missing function addresses
		int counter = 0;	// number of all functions

		CHECK_ERR( __libOpenGL.Load() );
		
		// CORE FUNCTIONS //
		#define GLES_GETPROC( _retType, _funcName, _funcParams, _retValue ) \
			OPENGLES_GET_PROC( PFNGL##_funcName##PROC,	gl##_funcName,	&glDefault##_funcName );

		#undef  GLES_GETCONST_I
		#define GLES_GETCONST_I( _type, _name ) \
			GL_CALL( glGetIntegerv( GL_##_name, &C_GL_##_name ) );
		
		#undef  GLES_GETCONST_F
		#define GLES_GETCONST_F( _type, _name ) \
			GL_CALL( glGetFloatv( GL_##_name, &C_GL_##_name ) );

		#define GLES_INIT_EXT_CONST_I( _namespace, _name ) \
			if ( _namespace::IsSupported() ) { \
				GL_CALL( glGetIntegerv( _namespace::GL_##_name, &_namespace::C_GL_##_name ) ); \
			}
		
		#define GLES_INIT_EXT_CONST_F( _namespace, _name ) \
			if ( _namespace::IsSupported() ) { \
				GL_CALL( glGetFloatv( _namespace::GL_##_name, &_namespace::C_GL_##_name ) ); \
			}

		GLES_CORE_FUNCTIONS( GLES_GETPROC )
		
		OES_get_program_binary::LoadExtension();
		OES_texture_3D::LoadExtension();
		OES_vertex_array_object::LoadExtension();
		EXT_debug_label::LoadExtension();
		EXT_debug_marker::LoadExtension();
		EXT_discard_framebuffer::LoadExtension();
		EXT_multisampled_render_to_texture::LoadExtension();
		EXT_multi_draw_arrays::LoadExtension();
		EXT_occlusion_query_boolean::LoadExtension();
		EXT_robustness::LoadExtension();
		EXT_separate_shader_objects::LoadExtension();
		EXT_texture_storage::LoadExtension();
		
		_ClearExtensions();

		// init extensions constants //
		GLES_CONSTANTS_I( GLES_GETCONST_I )
	
		GLES_INIT_EXT_CONST_I(  OES_texture_3D,						MAX_3D_TEXTURE_SIZE )
		GLES_INIT_EXT_CONST_I(  EXT_multisampled_render_to_texture,	MAX_SAMPLES )
		GLES_INIT_EXT_CONST_F(  EXT_texture_filter_anisotropic,		MAX_TEXTURE_MAX_ANISOTROPY )

		if ( missing > 0 )
		{
			WARNING( (string("While get address of OpenGLES functions: ") <<
					 missing << " missing, " << (counter - missing) << " successfuly.").cstr() );
		}

		_glesVersion = _ParseGLESVersion();
		return true;
	}
	
/*
=================================================
	GLES_Delete
=================================================
*/
	GLES_API void GLES_Delete ()
	{
		// CORE FUNCTIONS //
		#define GLES_ZEROFUNC( _retType, _funcName, _funcParams, _retValue ) \
			gl##_funcName = &glDefault##_funcName;

		GLES_CORE_FUNCTIONS( GLES_ZEROFUNC )
				
		OES_get_program_binary::UnloadExtension();
		OES_texture_3D::UnloadExtension();
		OES_vertex_array_object::UnloadExtension();
		EXT_debug_label::UnloadExtension();
		EXT_debug_marker::UnloadExtension();
		EXT_discard_framebuffer::UnloadExtension();
		EXT_multisampled_render_to_texture::UnloadExtension();
		EXT_multi_draw_arrays::UnloadExtension();
		EXT_occlusion_query_boolean::UnloadExtension();
		EXT_robustness::UnloadExtension();
		EXT_separate_shader_objects::UnloadExtension();
		EXT_texture_storage::UnloadExtension();

		_ClearExtensions();

		__libOpenGL.Unload();
	}

/*
=================================================
	init extensions
=================================================
*/
#	define _IS_SUPPORTED( _name ) \
		_IsExtSupported( _name )

#	define GLES_CHECK_SUPPORTED( _name, _gl_name ) \
		namespace _name { \
			\
			static int	s_iExtension_##_name = 0; \
			\
			GLES_API bool IsSupported() \
			{ \
				if ( s_iExtension_##_name == 0 ) { \
					s_iExtension_##_name = _IS_SUPPORTED( GLES_SWITCH( "GL_"#_name, "GL_"#_gl_name ) ) + 1; \
				} \
				return s_iExtension_##_name == 2; \
			} \
		}

#	define GLES_INIT_EXTENSION( _name, _gl_name, _funcs ) \
		GLES_CHECK_SUPPORTED( _name, _gl_name ) \
		namespace _name { \
		\
			GLES_API bool LoadExtension() \
			{ \
				int	missing = 0; \
				int counter = 0; \
				_funcs( GLES_GETPROC ) \
				return missing == 0; \
			} \
			\
			GLES_API void UnloadExtension() \
			{ \
				_funcs( GLES_ZEROFUNC ) \
				s_iExtension_##_name = 0; \
			} \
		}



	GLES_CHECK_SUPPORTED( OES_compressed_ETC1_RGB8_texture,		UNKNOWN							)
	GLES_CHECK_SUPPORTED( OES_compressed_paletted_texture,		UNKNOWN							)
	GLES_CHECK_SUPPORTED( OES_depth24,							ARB_depth_buffer_float			)
	GLES_CHECK_SUPPORTED( OES_depth32,							ARB_depth_buffer_float			)
	GLES_CHECK_SUPPORTED( OES_element_index_uint,				EXT_gpu_shader4					)
	GLES_INIT_EXTENSION(  OES_get_program_binary,				ARB_get_program_binary,			GLES_GET_PROGRAM_BINARY )
	GLES_CHECK_SUPPORTED( OES_mapbuffer,						ARB_map_buffer_range			)
	GLES_CHECK_SUPPORTED( OES_packed_depth_stencil,				EXT_packed_depth_stencil		)
	GLES_CHECK_SUPPORTED( OES_rgb8_rgba8,						ARB_framebuffer_object			)
	GLES_CHECK_SUPPORTED( OES_standard_derivatives,				UNKNOWN							)
	GLES_CHECK_SUPPORTED( OES_stencil1,							EXT_gpu_shader4					)
	GLES_CHECK_SUPPORTED( OES_stencil4,							EXT_gpu_shader4					)
	GLES_INIT_EXTENSION(  OES_texture_3D,						EXT_texture3D,					GLES_TEXTURE_3D							)
	GLES_CHECK_SUPPORTED( OES_texture_half_float,				ARB_half_float_pixel			)
	GLES_CHECK_SUPPORTED( OES_texture_float,					ARB_texture_float				)
	GLES_CHECK_SUPPORTED( OES_vertex_half_float,				ARB_half_float_vertex			)
	GLES_INIT_EXTENSION(  OES_vertex_array_object,				ARB_vertex_array_object,		GLES_VERTEX_ARRAY_OBJECT				)
	GLES_CHECK_SUPPORTED( OES_vertex_type_10_10_10_2,			ARB_vertex_type_2_10_10_10_rev	)
	GLES_CHECK_SUPPORTED( EXT_blend_minmax,						EXT_blend_minmax				)
	GLES_CHECK_SUPPORTED( EXT_color_buffer_half_float,			ARB_half_float_pixel			)
	GLES_INIT_EXTENSION(  EXT_debug_label,						ARB_debug_label,				GLES_DEBUG_LABEL						)
	GLES_INIT_EXTENSION(  EXT_debug_marker,						UNKNOWN,						GLES_DEBUG_MARKER						)
	GLES_INIT_EXTENSION(  EXT_discard_framebuffer,				UNKNOWN,						GLES_DISCARD_FRAMEBUFFER				)
	GLES_INIT_EXTENSION(  EXT_multisampled_render_to_texture,	UNKNOWN,						GLES_MULTISAMPLED_RENDER_TO_TEXTURE		)
	GLES_INIT_EXTENSION(  EXT_multi_draw_arrays,				EXT_multi_draw_arrays,			GLES_MULTI_DRAW_ARRAYS					)
	GLES_INIT_EXTENSION(  EXT_occlusion_query_boolean,			ARB_occlusion_query,			GLES_OCCLUSION_QUERY_BOOLEAN			)
	GLES_CHECK_SUPPORTED( EXT_read_format_bgra,					EXT_gpu_shader4					)
	GLES_INIT_EXTENSION(  EXT_robustness,						UNKNOWN,						GLES_ROBUSTNESS							)
	GLES_INIT_EXTENSION(  EXT_separate_shader_objects,			ARB_separate_shader_objects,	GLES_SEPARATE_SHADER_OBJECTS			)
	GLES_CHECK_SUPPORTED( EXT_shadow_samplers,					ARB_shadow						)
	GLES_CHECK_SUPPORTED( EXT_sRGB,								ARB_framebuffer_sRGB			)
	GLES_CHECK_SUPPORTED( EXT_texture_compression_dxt1,			EXT_texture_compression_dxt1	)
	GLES_CHECK_SUPPORTED( EXT_texture_filter_anisotropic,		EXT_texture_filter_anisotropic	)
	GLES_CHECK_SUPPORTED( EXT_texture_format_BGRA8888,			EXT_bgra						)
	GLES_CHECK_SUPPORTED( EXT_texture_rg,						ARB_texture_rg					)
	GLES_INIT_EXTENSION(  EXT_texture_storage,					ARB_texture_storage,			GLES_TEXTURE_STORAGE )
	GLES_CHECK_SUPPORTED( EXT_frag_depth,						UNKNOWN							)
	GLES_CHECK_SUPPORTED( OES_fragment_precision_high,			EXT_gpu_shader4					)
	
/*
=================================================
	clear extensions
=================================================
*/
#	define GLES_CLEAR_EXTENSION( _name ) \
		_name::s_iExtension_##_name = 0;

	void _ClearExtensions()
	{
		GLES_CLEAR_EXTENSION( OES_compressed_ETC1_RGB8_texture )
		GLES_CLEAR_EXTENSION( OES_compressed_paletted_texture )
		GLES_CLEAR_EXTENSION( OES_depth24 )
		GLES_CLEAR_EXTENSION( OES_depth32 )
		GLES_CLEAR_EXTENSION( OES_element_index_uint )
		GLES_CLEAR_EXTENSION( OES_mapbuffer )
		GLES_CLEAR_EXTENSION( OES_packed_depth_stencil )
		GLES_CLEAR_EXTENSION( OES_rgb8_rgba8 )
		GLES_CLEAR_EXTENSION( OES_standard_derivatives )
		GLES_CLEAR_EXTENSION( OES_stencil1 )
		GLES_CLEAR_EXTENSION( OES_stencil4 )
		GLES_CLEAR_EXTENSION( OES_texture_float )
		GLES_CLEAR_EXTENSION( OES_texture_half_float )
		GLES_CLEAR_EXTENSION( OES_vertex_half_float )
		GLES_CLEAR_EXTENSION( OES_vertex_type_10_10_10_2 )
		GLES_CLEAR_EXTENSION( EXT_blend_minmax )
		GLES_CLEAR_EXTENSION( EXT_color_buffer_half_float )
		GLES_CLEAR_EXTENSION( EXT_read_format_bgra )
		GLES_CLEAR_EXTENSION( EXT_shadow_samplers )
		GLES_CLEAR_EXTENSION( EXT_sRGB )
		GLES_CLEAR_EXTENSION( EXT_texture_compression_dxt1 )
		GLES_CLEAR_EXTENSION( EXT_texture_filter_anisotropic )
		GLES_CLEAR_EXTENSION( EXT_texture_format_BGRA8888 )
		GLES_CLEAR_EXTENSION( EXT_texture_rg )
		GLES_CLEAR_EXTENSION( EXT_texture_storage )
		GLES_CLEAR_EXTENSION( EXT_frag_depth )
		GLES_CLEAR_EXTENSION( OES_fragment_precision_high )
		
		#define GLES_CLEAR_EXT_CONST( _namespace, _name, _type ) \
			_namespace::C_GL_##_name = _type();
		
		GLES_CLEAR_EXT_CONST(  OES_texture_3D,						MAX_3D_TEXTURE_SIZE,		 GLint )
		GLES_CLEAR_EXT_CONST(  EXT_multisampled_render_to_texture,  MAX_SAMPLES,				 GLint )
		GLES_CLEAR_EXT_CONST(  EXT_texture_filter_anisotropic,		MAX_TEXTURE_MAX_ANISOTROPY,  GLfloat )

		s_sExtensions = uni_c_string();
	}
	
//-------------------------------------------------------------------

}	// gles
